package com.oa.process.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.oa.model.process.Process;
import com.oa.model.process.ProcessRecord;
import com.oa.model.process.ProcessTemplate;
import com.oa.model.system.SysUser;
import com.oa.process.mapper.OaProcessMapper;
import com.oa.process.service.OaProcessRecordService;
import com.oa.process.service.OaProcessService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.oa.process.service.OaProcessTemplateService;
import com.oa.security.custom.LoginUserInfoHelper;
import com.oa.system.service.SysUserService;
import com.oa.vo.process.ApprovalVo;
import com.oa.vo.process.ProcessFormVo;
import com.oa.vo.process.ProcessQueryVo;
import com.oa.vo.process.ProcessVo;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.EndEvent;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.task.Task;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.io.InputStream;
import java.util.*;
import java.util.zip.ZipInputStream;


/**
 * <p>
 * 审批类型 服务实现类
 * </p>
 *
 * @author cloud_oa
 * @since 2024-11-05
 */
@Service
@Slf4j
public class OaProcessServiceImpl extends ServiceImpl<OaProcessMapper, Process> implements OaProcessService {
    @Autowired
    private OaProcessMapper oaProcessMapper;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private OaProcessTemplateService oaProcessTemplateService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private OaProcessRecordService oaProcessRecordService;
    @Autowired
    private HistoryService historyService;
    @Override
    public void deployByZip(String deployPath) {
    //    定义zip输入流
        InputStream inputStream = this.getClass()
                .getClassLoader()
                .getResourceAsStream(deployPath);
        ZipInputStream zipInputStream = new ZipInputStream(inputStream);
    //    流程部署
        Deployment deployment = repositoryService.createDeployment()
                .addZipInputStream(zipInputStream)
                .deploy();
    }

    @Override
    public IPage<ProcessVo> selectPage(Page<ProcessVo> pageParam, ProcessQueryVo processQueryVo) {
        IPage<ProcessVo> page = oaProcessMapper.selectPage(pageParam,processQueryVo);
        return page;
    }

    @Transactional
    @Override
    public void startUp(ProcessFormVo processFormVo) {
        // 1 根据当前用户id获取用户信息
        SysUser sysUser = sysUserService.getById(LoginUserInfoHelper.getUserId());
        // 2 根据审批模板id 把模板信息查询
        ProcessTemplate processTemplate = oaProcessTemplateService.getById(processFormVo.getProcessTemplateId());
        // 3 保存提交审批信息到业务表中 oa_process
        Process process = new Process();
        //processFormVo复制到 转换 process
        BeanUtils.copyProperties(processFormVo,process);
        //其他值
        process.setStatus(1);//审批中
        String workNo = System.currentTimeMillis() + "";
        process.setProcessCode(workNo);
        process.setUserId(LoginUserInfoHelper.getUserId());
        process.setFormValues(processFormVo.getFormValues());
        process.setTitle(sysUser.getName() + "发起"
                + processTemplate.getName() + "申请");
        baseMapper.insert(process);
        // 4 启动流程实例 RuntimeService
        // 4.1 包含流程定义key 业务key
        // 4.2 设置流程参数 form表单json数据  转换map集合
        String processDefinitionKey = processTemplate.getProcessDefinitionKey();
        String businessKey = String.valueOf(process.getId());
        String formValues = processFormVo.getFormValues();
        //formData
        JSONObject jsonObject = JSON.parseObject(formValues);
        JSONObject formData = jsonObject.getJSONObject("formData");
        //遍历formData 得到内容 放入map
        Map<String, Object> map = new HashMap<>();
        for(Map.Entry<String,Object> entry : formData.entrySet()){
            map.put(entry.getKey(),entry.getValue());
        }
        Map<String,Object> variables  = new HashMap<>();
        variables.put("data",map);
        //启动流程实例
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, variables);
        // 5 查询下一个审批人  审批人可能多个
        List<Task> taskList = this.getCurrentTaskList(processInstance.getId());
        List<String> nameList = new ArrayList<>();
        for(Task task : taskList){
            String assigneeName = task.getAssignee();
            SysUser user = sysUserService.getByUserName(assigneeName);
            String name = user.getName();
            nameList.add(name);
            //推送消息

        }
        // 6 审批人发送消息 推送消息
        // 7 业务和流程关联 保存审批信息 oa_process_record
        process.setProcessInstanceId(processInstance.getId());
        process.setDescription("等待" + StringUtils.join(nameList.toArray(),"," )+ "审批" + processInstance.getDescription());
        baseMapper.updateById(process);
        //记录操作审批信息记录
        oaProcessRecordService.record(process.getId(), 1,"发起申请");
    }

    @Override
    public IPage<ProcessVo> findPending(Page<Process> pageParam) {
        /**
         * 1   封装查询条件 查询当前人的用户名称
         * 2   调用方法分页条件查询 返回list 待办任务集合
         * 3   封装返回值 到List<ProcessVo>里
         * 4   封装返回IPage对象
         */
        // 根据当前人id查询
        TaskQuery query = taskService.createTaskQuery().taskAssignee(LoginUserInfoHelper.getUsername()).orderByTaskCreateTime().desc();
        //listPage  方法两个参数  第一个参数 开始位置 第二个参数  每页显示记录数
        List<Task> list = query.listPage((int) ((pageParam.getCurrent() - 1) * pageParam.getSize()), (int) pageParam.getSize());
        long totalCount = query.count();
        List<ProcessVo> processList = new ArrayList<>();
        //根据流程的业务id 查询实体并关联
        /**
         *  从item 对象获取流程实例id
         *  根据流程实例id获取实例对象
         *  从流程实例对象获取业务key
         *  根据业务key获取Process对象
         *  Process对象 复制到ProcessVo对象
         */
        for (Task item : list) {
            String processInstanceId = item.getProcessInstanceId();
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            if(processInstance == null){
                continue;
            }

            //业务key
            String businessKey = processInstance.getBusinessKey();
            if(businessKey == null){
                continue;
            }
            //Process process = this.getById(Long.parseLong(businessKey));
            Process process = baseMapper.selectById(Long.parseLong(businessKey));
            ProcessVo processVo = new ProcessVo();
            BeanUtils.copyProperties(process,processVo);
            processVo.setTaskId(item.getId());
            processList.add(processVo);
        }
        IPage<ProcessVo> page = new Page<>(pageParam.getCurrent(), pageParam.getSize(), totalCount);
        page.setRecords(processList);
        return page;
    }
    //当前任务列表
    private List<Task> getCurrentTaskList(String id) {
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(id).list();
        return taskList;
    }

    /**
     * 获取审批详情
     * @param id
     * @return
     */
    @Override
    public Map<String, Object> show(Long id) {
        /**
         *  1 根据流程id获取流程信息Process
         *  2 根据流程id获取流程记录信息
         *  3 根据模板id查询模板信息
         *  4 判读当前用户是否可以审批
         *  5 封装map数据返回
         */
        //Process process = this.getById(id);
        Process process = baseMapper.selectById(id);
        LambdaQueryWrapper<ProcessRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ProcessRecord::getProcessId,id);
        List<ProcessRecord> processRecordList = oaProcessRecordService.list(wrapper);
        //List<ProcessRecord> processRecordList = oaProcessRecordService
        //        .list(new LambdaQueryWrapper<ProcessRecord>().eq(ProcessRecord::getProcessId, id));
        ProcessTemplate processTemplate = oaProcessTemplateService.getById(process.getProcessTemplateId());
        //计算当前用户是否可以审批 能够查看详情的用户是不是都能审批 审批后不能重复审批
        boolean isApprove = false;
        List<Task> taskList = this.getCurrentTaskList(process.getProcessInstanceId());
        if(!CollectionUtils.isEmpty(taskList)){
            for(Task task: taskList){
                //判断任务审批人是否当前用户
                if(task.getAssignee().equals(LoginUserInfoHelper.getUsername())){
                    isApprove = true;
                }
            }
        }

        Map<String, Object> map = new HashMap<>();
        map.put("process",process);
        map.put("processRecordList",processRecordList);
        map.put("processTemplate",processTemplate);
        map.put("isApprove",isApprove);
        return map;
    }

    /**
     * 审批接口实现
     * @param approvalVo
     */
    @Override
    public void approve(ApprovalVo approvalVo) {
        //从approvalVo获取任务id 根据任务id获取流程变量
        Map<String, Object> variables1 = taskService.getVariables(approvalVo.getTaskId());
        for(Map.Entry<String,Object> entry : variables1.entrySet()){
            System.out.println("key=" + entry.getKey() + " and value= " + entry.getValue());
        }
        //获取审批任务  判断审批状态值  状态值1  审批通过  状态值-1  驳回 流程结束
        String taskId  = approvalVo.getTaskId();
        if(approvalVo.getStatus() == 1){
            //已通过
            Map<String,Object> vairables = new HashMap<String,Object>();
            taskService.complete(taskId,vairables);
        }else {
            //驳回
            this.endTask(taskId);
        }
        //记录审批过程相关信息
        String description = approvalVo.getStatus().intValue() == 1 ? "已通过" : "驳回";
        oaProcessRecordService.record(approvalVo.getProcessId(), approvalVo.getStatus(),description);
        // 计算下一个审批人 更新流程表记录  process表记录
        //Process process = this.getById(approvalVo.getProcessId());
        Process process = baseMapper.selectById(approvalVo.getProcessId());
        List<Task> taskList = this.getCurrentTaskList(process.getProcessInstanceId());
        if(!CollectionUtils.isEmpty(taskList)){
            List<String> assigneeList = new ArrayList<>();
            for(Task task : taskList){
                SysUser sysUser = sysUserService.getByUserName(task.getAssignee());
                assigneeList.add(sysUser.getName());
                //推送消息给下一个审批人
            }
            //更新process流程信息
            process.setDescription("等待" + StringUtils.join(assigneeList.toArray(),",")+ "审批");
            process.setStatus(1);
        }else{
            if(approvalVo.getStatus().intValue() == 1){
                process.setDescription("审批完成(同意)");
                process.setStatus(2);
            }else{
                process.setDescription("审批完成(驳回)");
                process.setStatus(-1);
            }
        }
        //推送消息给申请人
        this.updateById(process);
    }

    /**
     * 结束流程
     * @param taskId
     */
    private void endTask(String taskId) {
        //根据任务id获取任务对象task
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        //获取流程定义模型bpmnModel
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        //获取结束流向节点
        List<EndEvent> endEventList = bpmnModel.getMainProcess().findFlowElementsOfType(EndEvent.class);

        if(CollectionUtils.isEmpty(endEventList)){
            return;
        }
        FlowNode endFlowNode = (FlowNode) endEventList.get(0);
        //当前流向节点
        FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess()
                .getFlowElement(task.getTaskDefinitionKey());

        //临时保存当前活动的原始方向
        List originalSequenceFlowList = new ArrayList<>();
        originalSequenceFlowList.addAll(currentFlowNode.getOutgoingFlows());
        //清空当前活动的方向
        currentFlowNode.getOutgoingFlows().clear();

        //建立新方向
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlow");
        newSequenceFlow.setSourceFlowElement(currentFlowNode);
        newSequenceFlow.setTargetFlowElement(endFlowNode);
        List newSequenceFlowList = new ArrayList<>();
        newSequenceFlowList.add(newSequenceFlow);

        // 当前节点指向的新方向
        currentFlowNode.setOutgoingFlows(newSequenceFlowList);
        //完成当前任务
        taskService.complete(task.getId());
    }

    /**
     * 查询已处理流程
     * @param pageParam
     * @return
     */
    @Override
    public IPage<ProcessVo> findProcessed(Page<Process> pageParam) {
        //根据当前人id查询
        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery().taskAssignee(LoginUserInfoHelper.getUsername()).finished().orderByTaskCreateTime().desc();
        List<HistoricTaskInstance> list = query.listPage((int) ((pageParam.getCurrent() - 1) * pageParam.getSize()), (int) pageParam.getSize());
        long totalCount = query.count();

        List<ProcessVo> processList = new ArrayList<>();
        for(HistoricTaskInstance item : list) {
            String processInstanceId = item.getProcessInstanceId();
            Process process = this.getOne(new LambdaQueryWrapper<Process>().eq(Process::getProcessInstanceId, processInstanceId));
            ProcessVo processVo = new ProcessVo();
            BeanUtils.copyProperties(process, processVo);
            processVo.setTaskId("0");
            processList.add(processVo);
        }
        IPage<ProcessVo> page = new Page<ProcessVo>(pageParam.getCurrent(),pageParam.getSize(),totalCount);
        page.setRecords(processList);
        return page;
    }

    /**
     * 查询已发起流程列表
     * @param pageParam
     * @return
     */
    @Override
    public IPage<ProcessVo> findStarted(Page<ProcessVo> pageParam) {
        ProcessQueryVo processQueryVo = new ProcessQueryVo();
        processQueryVo.setUserId(LoginUserInfoHelper.getUserId());
        IPage<ProcessVo> page = oaProcessMapper.selectPage(pageParam,processQueryVo);

        for(ProcessVo item : page.getRecords()) {
            item.setTaskId("0");
        }
        return page;
    }
}
